/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using STSdb.General.Buffers;
using STSdb.General.Collections;
using STSdb.Files;
using STSdb.Persist;
using System.Linq.Expressions;
using System.Reflection;

namespace STSdb.Data
{
    public partial class FTable<TKey, TRecord> : ITransaction, IEnumerable<Row<TKey, TRecord>>
    {
        private const ulong HALF_HEADER_SIZE = 128;
        internal const ulong HEADER_SIZE = sizeof(byte) + 2 * HALF_HEADER_SIZE;

        internal byte ActiveHeader;
        private LinkWrapper RootWrapper;
        private uint blockCapacity;
        private ulong RecordPersistPosition;
        internal IBinaryPersist<IIndexer<TRecord>> recordPersist;
        private AccessMode Mode;
        private ulong KeyMapPosition;
        private IKeyMap<TKey> keyMap;
        private uint depth;
        private uint cacheCapacity;

        internal LinkedList<Link> Cache = new LinkedList<Link>();
        internal byte[] MinIndex;
        internal byte[] MaxIndex;

        public ushort Version { get; private set; }
        public ulong Count { get; private set; }
        public ulong SystemSize { get; private set; }
        public ulong DataSize { get; private set; }
        public bool IsModified { get; private set; }

        public readonly RawFile RawFile;
        public readonly ulong Offset;

        public FTable(RawFile rawFile, ulong offset)
        {
            if (rawFile == null)
                throw new ArgumentNullException("rawFile");

            RawFile = rawFile;
            Offset = offset;
            RootWrapper = new LinkWrapper();

            BinaryFile binaryFile = new BinaryFile(RawFile);
            binaryFile.Position = Offset;
            ActiveHeader = binaryFile.ReadByte();

            LoadHeader(ActiveHeader);
            switch (Version)
            {
                case 0: //it's a new file
                    {
                        Version = 1;
                        SystemSize = HEADER_SIZE;
                        SaveHeader(ActiveHeader);
                        binaryFile.Position = Offset + unchecked(HEADER_SIZE - 1);
                        binaryFile.Write((byte)0); //marker for end of the header
                        RawFile.Flush();
                    }
                    break;
                case 1:
                    {
                    }
                    break;
                default:
                    throw new Exception(String.Format("Unknown version {0}", Version));
            }

            IsModified = false;
        }

        #region Manage

        public TRecord this[byte[] index]
        {
            get
            {
                if (index.Length != Depth)
                    throw new ArgumentException("index.Length != Depth");

                Mode = FTable<TKey, TRecord>.AccessMode.Direct;

                return Root.Collection[index];
            }
            set
            {
                if (index.Length != Depth)
                    throw new ArgumentException("index.Length != Depth");

                Mode = FTable<TKey, TRecord>.AccessMode.Direct;

                Root.Collection[index] = value;
            }
        }

        public TRecord this[TKey key]
        {
            get
            {
                byte[] index = KeyMap.Direct(key);

                return this[index];
            }
            set
            {
                byte[] index = KeyMap.Direct(key);

                this[index] = value;
            }
        }

        public void Delete(byte[] index)
        {
            if (index.Length != Depth)
                throw new ArgumentException("index.Length != Depth");

            Mode = FTable<TKey, TRecord>.AccessMode.Direct;

            Root.Collection.Delete(index);
        }

        public void Delete(TKey key)
        {
            byte[] index = KeyMap.Direct(key);

            Delete(index);
        }

        public void Delete(byte[] firstIndex, byte[] lastIndex)
        {
            if (firstIndex.Length != Depth)
                throw new ArgumentException("firstIndex.Length != Depth");
            if (lastIndex.Length != Depth)
                throw new ArgumentException("lastIndex.Length != Depth");

            var snapshot = Snapshot();
            foreach (var row in snapshot.Forward(firstIndex, lastIndex))
                Delete(row.Key);
        }

        public void Delete(TKey firstKey, TKey lastKey)
        {
            byte[] firstIndex = KeyMap.Direct(firstKey);
            byte[] lastIndex = KeyMap.Direct(lastKey);

            Delete(firstIndex, lastIndex);
        }

        public void Clear()
        {
            if (Count == 0)
                return;

            Cache.Clear();

            Count = 0;
            SystemSize = HEADER_SIZE;
            DataSize = 0;

            RootWrapper = new LinkWrapper();

            IsModified = true;
        }

        public void Close()
        {
        }

        public void Merge(XTable<TKey, TRecord> table)
        {
            foreach (var row in table)
                this[row.Key] = row.Record;
        }

        public void Change(byte[] index, byte[] newIndex)
        {
            TRecord record = this[index];
            Delete(index);
            this[newIndex] = record;
        }

        public void Change(TKey key, TKey newKey)
        {
            byte[] index = KeyMap.Direct(key);
            byte[] newIndex = KeyMap.Direct(key);

            Change(index, newIndex);
        }

        public void Swap(byte[] index1, byte[] index2)
        {
            TRecord record1 = this[index1];
            TRecord record2 = this[index2];
            this[index1] = record2;
            this[index2] = record1;
        }

        public void Swap(TKey key1, TKey key2)
        {
            byte[] index1 = KeyMap.Direct(key1);
            byte[] index2 = KeyMap.Direct(key2);

            Swap(index1, index2);
        }

        #endregion

        #region Find Methods

        public bool TryGet(byte[] index, out TRecord record)
        {
            if (index.Length != Depth)
                throw new ArgumentException("index.Length != Depth");

            Mode = FTable<TKey, TRecord>.AccessMode.Direct;

            return Root.Collection.TryGet(index, out record);
        }

        public bool TryGet(TKey key, out TRecord record)
        {
            byte[] index = KeyMap.Direct(key);

            return TryGet(index, out record);
        }

        public TRecord Find(TKey key)
        {
            TRecord record;
            TryGet(key, out record);

            return record;
        }

        public bool Exists(TKey key)
        {
            TRecord record;
            return TryGet(key, out record);
        }

        public Row<TKey, TRecord> FindNext(byte[] index)
        {
            return Forward(index, MaxIndex).FirstOrDefault();
        }

        public Row<TKey, TRecord> FindNext(TKey key)
        {
            byte[] index = KeyMap.Direct(key);
            return FindNext(index);
        }

        public Row<TKey, TRecord> FindPrev(byte[] index)
        {
            return Backward(index, MinIndex).FirstOrDefault();
        }

        public Row<TKey, TRecord> FindPrev(TKey key)
        {
            byte[] index = KeyMap.Direct(key);
            return FindPrev(index);
        }

        public Row<TKey, TRecord> FindAfter(TKey key)
        {
            byte[] index = KeyMap.Direct(key);
            if (index.Increment())
                return null;

            return FindNext(index);
        }

        public Row<TKey, TRecord> FindBefore(TKey key)
        {
            byte[] index = KeyMap.Direct(key);
            if (index.Decrement())
                return null;

            return FindPrev(index);
        }

        //public Row<TKey, TRecord> SkipForward(ulong count)
        //{
        //    if (count >= Count)
        //        return null;

        //    Mode = FTable<TKey, TRecord>.AccessMode.Sequential;
           
        //}

        #endregion

        #region Enumerators

        public IEnumerable<Row<TKey, TRecord>> Forward(byte[] firstIndex, byte[] lastIndex)
        {
            if (firstIndex.Length > Depth)
                throw new ArgumentException("firstIndex.Length > Depth");
            else if (firstIndex.Length < Depth)
            {
                int idx = (int)Depth - firstIndex.Length;
                byte[] index = new byte[Depth];
                firstIndex.CopyTo(index, idx);
                //for (int i = 0; i < idx; i++)
                //{
                //    index[i] = byte.MinValue;
                //}
                firstIndex = index;
            }

            if (lastIndex.Length > Depth)
                throw new ArgumentException("lastIndex.Length > Depth");
            else if (lastIndex.Length < Depth)
            {
                int idx = (int)Depth - lastIndex.Length;
                byte[] index = new byte[Depth];
                lastIndex.CopyTo(index, idx);
                for (int i = 0; i < idx; i++)
                {
                    index[i] = byte.MaxValue;
                }
                lastIndex = index;
            }

            Mode = FTable<TKey, TRecord>.AccessMode.Sequential;

            foreach (var row in Root.Collection.Forward(firstIndex, lastIndex))
            {
                if (row.Key.ReverseCompareTo(lastIndex) > 0)
                    yield break;

                yield return new Row<TKey, TRecord>(KeyMap.Reverse(row.Key), row.Record);
            }
        }

        public IEnumerable<Row<TKey, TRecord>> Backward(byte[] lastIndex, byte[] firstIndex)
        {
            if (firstIndex.Length > Depth)
                throw new ArgumentException("firstIndex.Length > Depth");
            else if (firstIndex.Length < Depth)
            {
                int idx = (int)Depth - firstIndex.Length;
                byte[] index = new byte[Depth];
                firstIndex.CopyTo(index, idx);
                //for (int i = 0; i < idx; i++)
                //{
                //    index[i] = byte.MinValue;
                //}
                firstIndex = index;
            }

            if (lastIndex.Length > Depth)
                throw new ArgumentException("lastIndex.Length > Depth");
            else if (lastIndex.Length < Depth)
            {
                int idx = (int)Depth - lastIndex.Length;
                byte[] index = new byte[Depth];
                lastIndex.CopyTo(index, idx);
                for (int i = 0; i < idx; i++)
                {
                    index[i] = byte.MaxValue;
                }
                lastIndex = index;
            }

            Mode = FTable<TKey, TRecord>.AccessMode.Sequential;

            foreach (var row in Root.Collection.Backward(lastIndex, firstIndex))
            {
                if (row.Key.ReverseCompareTo(firstIndex) < 0)
                    yield break;

                yield return new Row<TKey, TRecord>(KeyMap.Reverse(row.Key), row.Record);
            }
        }

        public IEnumerable<Row<TKey, TRecord>> Forward()
        {
            return Forward(MinIndex, MaxIndex);
        }

        public IEnumerable<Row<TKey, TRecord>> Backward()
        {
            return Backward(MaxIndex, MinIndex);
        }

        public IEnumerable<Row<TKey, TRecord>> Forward(TKey firstKey, TKey lastKey)
        {
            byte[] firstIndex = KeyMap.Direct(firstKey);
            byte[] lastIndex = KeyMap.Direct(lastKey);

            return Forward(firstIndex, lastIndex);
        }

        public IEnumerable<Row<TKey, TRecord>> Backward(TKey lastKey, TKey firstKey)
        {
            byte[] firstIndex = KeyMap.Direct(firstKey);
            byte[] lastIndex = KeyMap.Direct(lastKey);

            return Backward(lastIndex, firstIndex);
        }

        public IEnumerable<Row<TKey, TRecord>> Forward(TKey firstKey)
        {
            byte[] firstIndex = KeyMap.Direct(firstKey);
            return Forward(firstIndex, MaxIndex);
        }

        public IEnumerable<Row<TKey, TRecord>> Backward(TKey lastKey)
        {
            byte[] lastIndex = KeyMap.Direct(lastKey);
            return Backward(lastIndex, MinIndex);
        }

        /// <summary>
        /// This is equal to Forward(index, index);
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IEnumerable<Row<TKey, TRecord>> ForwardGroup(byte[] index)
        {
            return Forward(index, index);
        }

        /// <summary>
        /// This is equal to Backward(index, index);
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IEnumerable<Row<TKey, TRecord>> BackwardGroup(byte[] index)
        {
            return Backward(index, index);
        }

        /// <summary>
        /// This is equal to Forward(key, key);
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IEnumerable<Row<TKey, TRecord>> ForwardGroup(TKey key)
        {
            byte[] index = KeyMap.Direct(key);
            return Forward(index, index);
        }

        /// <summary>
        /// This is equal to Backward(key, key);
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IEnumerable<Row<TKey, TRecord>> BackwardGroup(TKey key)
        {
            byte[] index = KeyMap.Direct(key);
            return Backward(index, index);
        }

        //public IEnumerable<TKey> DistinctForwardGroup(byte[] index)
        //{
        //    byte[] buffer = (byte[])index.Clone();
        //    do
        //    {
        //        var row = Forward(buffer, MaxIndex).FirstOrDefault();
        //        if (row == null)
        //            yield break;

        //        yield return row.Key;
        //    }
        //    while (Increment(buffer));
        //}

        #region IEnumerable<Row<TKey,TRecord>> Members

        public IEnumerator<Row<TKey, TRecord>> GetEnumerator()
        {
            Mode = FTable<TKey, TRecord>.AccessMode.Direct;

            foreach (var row in Root.Collection)
                yield return new Row<TKey, TRecord>(KeyMap.Reverse(row.Key), row.Record);

            ClearOverflow(0);
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        public FTable<TKey, TRecord> Snapshot()
        {
            Flush();

            FTable<TKey, TRecord> table = new FTable<TKey, TRecord>(RawFile, RawFile.Length);
            table.Depth = this.Depth;
            table.RecordPersist = this.RecordPersist;
            table.KeyMap = this.KeyMap;
            table.Count = this.Count; //don't move it above the two lines.
            table.DataSize = this.DataSize;
            table.SystemSize = this.SystemSize;
            table.CacheCapacity = this.CacheCapacity;
            table.BlockCapacity = this.BlockCapacity;
            table.RootWrapper.Ptr = this.RootWrapper.Ptr;

            return table;
        }

        #endregion

        #region Cache

        public uint CacheSize
        {
            get { return (uint)Cache.Count; }
        }

        public uint CacheCapacity
        {
            get { return cacheCapacity; }
            set
            {
                if (value == 0)
                    value = 8 * 1024;

                if (cacheCapacity == value)
                    return;

                cacheCapacity = value >= Depth ? value : Depth;
                ClearOverflow();

                IsModified = true;
            }
        }

        internal void ClearOverflow()
        {
            ClearOverflow(CacheCapacity);
        }

        internal void ClearOverflow(uint capacity)
        {
            while (Cache.Count > capacity)
            {
                Link link = Cache.Last.Value;
                link.Save();
                link.__Link();
            }
        }

        public void Flush()
        {
            LinkedListNode<Link> link = Cache.Last;
            while (link != null)
            {
                link.Value.Save();

                link = link.Previous;
            }
        }

        #endregion

        #region Properties

        public IBinaryPersist<IIndexer<TRecord>> RecordPersist
        {
            get { return recordPersist; }
            set
            {
                if (Count > 0)
                    return;

                recordPersist = value;
                RecordPersistPosition = ulong.MaxValue; //recordPersist is modified.

                IsModified = true;
            }
        }

        public IKeyMap<TKey> KeyMap
        {
            get { return keyMap; }
            set
            {
                if (Count > 0)
                    return;

                keyMap = value;
                KeyMapPosition = ulong.MaxValue; //keyMap is modified.

                IsModified = true;
            }
        }

        public uint BlockCapacity
        {
            get { return blockCapacity; }
            set
            {
                if (value == 0)
                {
                    Type type = typeof(TRecord);
                    if (type == typeof(byte[]))
                        value = 1024;//???
                    else if (type == typeof(Byte))
                        value = 8192;
                    else if (type == typeof(SByte))
                        value = 8192;
                    else if (type == typeof(Boolean))
                        value = 8192;
                    else if (type == typeof(UInt16))
                        value = 4096;
                    else if (type == typeof(Int16))
                        value = 4096;
                    else if (type == typeof(UInt32))
                        value = 2048;
                    else if (type == typeof(Int32))
                        value = 2048;
                    else if (type == typeof(UInt64))
                        value = 1024;
                    else if (type == typeof(Int64))
                        value = 1024;
                    else if (type == typeof(Double))
                        value = 1024;
                    else if (type == typeof(Single))
                        value = 2048;
                    else if (type == typeof(Decimal))
                        value = 1024;
                    else if (type == typeof(DateTime))
                        value = 1024;
                    else if (type == typeof(String))
                        value = 1024;
                    else if (type.IsValueType && type.StructLayoutAttribute.Value != LayoutKind.Auto)
                        value = 1024; //(uint)(8192 / Marshal.SizeOf(type));
                    else
                        value = 1024;
                }

                if (blockCapacity == value)
                    return;

                blockCapacity = value;

                IsModified = true;
            }
        }

        private Link Root
        {
            get
            {
                LinkWrapper wrapper = RootWrapper;
                if (wrapper.Link != null)
                {
                    wrapper.Link.Touch();

                    return wrapper.Link;
                }

                wrapper.Link = new Link(this, null, wrapper);
                if (!wrapper.Ptr.IsNull)
                    wrapper.Link.Load();

                return wrapper.Link;
            }
        }

        public Row<TKey, TRecord> FirstRow
        {
            get { return Forward().FirstOrDefault(); }
        }

        public Row<TKey, TRecord> LastRow
        {
            get { return Backward().FirstOrDefault(); }
        }

        public uint Depth
        {
            get { return depth; }
            set
            {
                if (depth == value)
                    return;

                if (Count != 0)
                    throw new InvalidOperationException("Count > 0");

                depth = value;
                MinIndex = new byte[Depth];
                MaxIndex = new byte[Depth];

                for (int i = 0; i < Depth; i++)
                {
                    //MinIndex[i] = byte.MinValue;
                    MaxIndex[i] = byte.MaxValue;
                }

                if (CacheCapacity < Depth)
                    CacheCapacity = Depth;

                IsModified = true;
            }
        }

        public object SyncRoot
        {
            get { return RawFile.SyncRoot; }
        }

        public ulong Size
        {
            get { return SystemSize + DataSize; }
        }

        #endregion

        #region ITransaction Members

        public IEnumerable<JournalItem> Journal()
        {
            yield return new JournalItem(RawFile.Handle, Offset, BitConverter.GetBytes(ActiveHeader));
        }

        public void Commit()
        {
            if (!IsModified)
                return;

            Flush();

            RawFile.Flush();

            byte activeHeader = (byte)(ActiveHeader == 0 ? 1 : 0);
            SaveHeader(activeHeader);

            RawFile.Flush();

            BinaryFile binaryFile = new BinaryFile(RawFile);
            binaryFile.Position = Offset;
            binaryFile.Write(activeHeader);

            RawFile.Flush();

            IsModified = false;

            ActiveHeader = activeHeader;
        }

        public void Rollback()
        {
            Cache.Clear();
            RootWrapper.Link = null;

            LoadHeader(ActiveHeader);
            IsModified = false;
        }

        #endregion

        #region Save/Load

        private void SaveHeader(byte activeHeader)
        {
            BinaryFile binaryFile = new BinaryFile(RawFile);
            if (KeyMapPosition == ulong.MaxValue) //keyMap is modified, save.
            {
                KeyMapPosition = binaryFile.Position = binaryFile.Length;
                binaryFile.Write(KeyMap);
            }
            if (RecordPersistPosition == ulong.MaxValue) //recordPersist is modified, save.
            {
                RecordPersistPosition = binaryFile.Position = binaryFile.Length;
                binaryFile.Write(RecordPersist);
            }

            binaryFile.Position = Offset + sizeof(byte) + activeHeader * HALF_HEADER_SIZE;
            binaryFile.Write(Version);
            binaryFile.Write(Depth);
            binaryFile.Write(KeyMapPosition);
            binaryFile.Write(RecordPersistPosition);
            binaryFile.Write(BlockCapacity);
            binaryFile.Write(CacheCapacity);
            binaryFile.Write(RootWrapper.Ptr);
            binaryFile.Write(Count);
            binaryFile.Write(SystemSize);
            binaryFile.Write(DataSize);
        }

        private void LoadHeader(byte activeHeader)
        {
            BinaryFile binaryFile = new BinaryFile(RawFile);
            binaryFile.Position = Offset + sizeof(byte) + activeHeader * HALF_HEADER_SIZE;
            Version = binaryFile.ReadUInt16();
            Depth = binaryFile.ReadUInt32();
            KeyMapPosition = binaryFile.ReadUInt64();
            RecordPersistPosition = binaryFile.ReadUInt64();
            BlockCapacity = binaryFile.ReadUInt32();
            CacheCapacity = binaryFile.ReadUInt32();
            RootWrapper.Ptr = binaryFile.ReadPtr();
            Count = binaryFile.ReadUInt64();
            SystemSize = binaryFile.ReadUInt64();
            DataSize = binaryFile.ReadUInt64();

            if (KeyMapPosition == 0)
            {
                Type type = typeof(TKey);
                if (type == typeof(byte[]))
                    keyMap = (IKeyMap<TKey>)new ByteArrayKeyMap();
                else if (type == typeof(Byte))
                    keyMap = (IKeyMap<TKey>)new ByteKeyMap();
                else if (type == typeof(SByte))
                    keyMap = (IKeyMap<TKey>)new SByteKeyMap();
                else if (type == typeof(Boolean))
                    keyMap = (IKeyMap<TKey>)new BooleanKeyMap();
                else if (type == typeof(UInt16))
                    keyMap = (IKeyMap<TKey>)new UInt16KeyMap();
                else if (type == typeof(Int16))
                    keyMap = (IKeyMap<TKey>)new Int16KeyMap();
                else if (type == typeof(UInt32))
                    keyMap = (IKeyMap<TKey>)new UInt32KeyMap();
                else if (type == typeof(Int32))
                    keyMap = (IKeyMap<TKey>)new Int32KeyMap();
                else if (type == typeof(UInt64))
                    keyMap = (IKeyMap<TKey>)new UInt64KeyMap();
                else if (type == typeof(Int64))
                    keyMap = (IKeyMap<TKey>)new Int64KeyMap();
                else if (type == typeof(Double))
                    keyMap = (IKeyMap<TKey>)new DoubleKeyMap();
                else if (type == typeof(Single))
                    keyMap = (IKeyMap<TKey>)new SingleKeyMap();
                else if (type == typeof(Decimal))
                    keyMap = (IKeyMap<TKey>)new DecimalKeyMap();
                else if (type == typeof(DateTime))
                    keyMap = (IKeyMap<TKey>)new DateTimeKeyMap();
                else if (type == typeof(TimeSpan))
                    keyMap = (IKeyMap<TKey>)new TimeSpanKeyMap();
                else if (type == typeof(String))
                    keyMap = (IKeyMap<TKey>)new StringKeyMap(32);
                else if (type.IsValueType && type.StructLayoutAttribute.Value != LayoutKind.Auto)
                    keyMap = (IKeyMap<TKey>)new StructKeyMap<TKey>();
                else if (type == typeof(Locator))
                    keyMap = (IKeyMap<TKey>)new LocatorKeyMap();
                else if (type.Name.StartsWith("XKey`") && type.IsGenericType)
                {
                    Type[] generics = type.GetGenericArguments();
                    Type xKeyMapType = null;
                    switch (generics.Length)
                    {
                        case 2:
                            xKeyMapType = typeof(XKeyMap<,>).MakeGenericType(generics);
                            break;
                        case 3:
                            xKeyMapType = typeof(XKeyMap<,,>).MakeGenericType(generics);
                            break;
                        case 4:
                            xKeyMapType = typeof(XKeyMap<,,,>).MakeGenericType(generics);
                            break;
                        case 5:
                            xKeyMapType = typeof(XKeyMap<,,,,>).MakeGenericType(generics);
                            break;
                        case 6:
                            xKeyMapType = typeof(XKeyMap<,,,,,>).MakeGenericType(generics);
                            break;
                        case 7:
                            xKeyMapType = typeof(XKeyMap<,,,,,,>).MakeGenericType(generics);
                            break;
                    }

                    if (xKeyMapType != null)
                    {
                        int i;
                        for (i = 0; i < generics.Length; i++)
                        {
                            KeyMapBuilder builder = null;
                            
                            try
                            {
                                builder = KeyMapBuilder.Build(generics[i], null, 0);
                            }
                            catch (Exception)
                            {
                            }

                            if (builder == null || builder.Length == 0)
                            {
                                keyMap = null;
                                break;
                            }
                        }

                        if (i == generics.Length)
                            keyMap = (IKeyMap<TKey>)Activator.CreateInstance(xKeyMapType);
                    }
                }
                else
                    keyMap = null;
            }
            else
            {
                binaryFile.Position = KeyMapPosition;
                keyMap = (IKeyMap<TKey>)binaryFile.ReadObject();
            }

            if (RecordPersistPosition == 0)
                recordPersist = new DefaultBinaryPersist<TRecord>();
            else
            {
                binaryFile.Position = RecordPersistPosition;
                recordPersist = (IBinaryPersist<IIndexer<TRecord>>)binaryFile.ReadObject();
            }
        }

        #endregion

        private enum AccessMode : byte
        {
            Sequential,
            Direct
        }
    }
}
